package cn.jwtool.jwcache.buffer.data;


import cn.jwtool.jwcache.buffer.Publisher;
import cn.jwtool.jwcache.buffer.listener.Event;

import java.util.*;

/**
 * @author : willian fu
 * @version : 1.0
 * 数据操作类
 */

public class JwCatchTemplate implements CatchTemplate {

    /**
     * 从数据中获取一个值
     *
     * @param clazz key对应的value类型
     * @return value
     */
    @Override
    public <T> T getForClass(String key, Class<T> clazz) {
        if (getExpired(key).equals(0)){
            return null;
        }
        return clazz.cast(getData().get(key));
    }

    /**
     * 删除键
     *
     * @param key 键
     * @return 被删除的键的值
     */
    @Override
    public Object deleteKey(String key, boolean type) {
        if (getData().containsKey(key)){
            getExpired().remove(key);
            if (!type){
                //因为过期删除的话需要传播事件
                Publisher.publish(Event.KEY_EXPIRED, key);
            }
            return getData().remove(key);
        }
        return null;
    }

    /**
     * 获取剩余时长
     *
     * @param key 键
     * @return 剩余时长
     */
    @Override
    public Long getExpired(String key) {
        if (getExpired().containsKey(key)){
            Long death = getExpired().get(key);
            if (death < 0){
                return -1L;
            }
            long life = death - GregorianCalendar.getInstance().getTimeInMillis();
            if (life <= 0){
                //key过期
                deleteKey(key, false);
                return 0L;
            }
            return life;
        }
        return 0L;
    }

    /**
     * 添加一个value
     *
     * @param key   键
     * @param value 值
     * @param time  有效时间ms
     */
    @Override
    public void setValue(String key, Object value, Long time) {
        getData().put(key, value);
        getExpired().put(key, GregorianCalendar.getInstance().getTimeInMillis() + time);
    }

    /**
     * 更新key的过期时间
     *
     * @param key  键
     * @param time 值
     */
    @Override
    public Long updateExpired(String key, Long time) {
        if (!getExpired(key).equals(0L)){
            return getExpired().put(key, time);
        }
        return null;
    }

    /**
     * 获取所有key
     *
     * @return key集合
     */
    @Override
    public Set<String> getKeys() {
        Set<String> keys = Collections.synchronizedSet(new HashSet<>());
        long nowTime = GregorianCalendar.getInstance().getTimeInMillis();
        getExpired().forEach((k, v) -> {
            if(v.equals(-1L) || v - nowTime > 0L ){
                keys.add(k);
            }else {
                //遍历过程有key过期
                deleteKey(k, false);
            }
        });
        return keys;
    }

    /**
     * 模糊查询key,前缀模糊和后缀模糊
     *
     * @param rule **xxx xxx** **xxx**
     * @return 符合条件的key集合
     */
    @Override
    public Set<String> getKeys(String rule) {
        Set<String> keys = Collections.synchronizedSet(new HashSet<>());
        long nowTime = GregorianCalendar.getInstance().getTimeInMillis();
        getExpired().forEach((k, v) -> {
            if(v.equals(-1L) || v - nowTime > 0L ){
                if (match(rule, k)){
                    keys.add(k);
                }
            }else {
                //遍历过程有key过期
                deleteKey(k, false);
            }
        });
        return keys;
    }

    /**
     * 写AFO日志，持久化数据
     *
     * @param command 命令
     * @param params  参数
     */
    @Override
    public void writeAFOLog(String command, Object params) {

    }

    @Override
    public int getSize() {
        return getData().size();
    }

    /**
     * 模糊匹配
     * @param rule 匹配规则
     * @param target 对比数据
     * @return 匹配结果
     */
    private static boolean match(String rule, String target){
        if (rule.startsWith("**")){
            if (rule.endsWith("**")){
                return target.contains(rule.substring(2, rule.length()-2));
            }
            return target.endsWith(rule.substring(2));
        }else if (rule.endsWith("**")){
            return target.startsWith(rule.substring(0,rule.length()-2));
        }else {
            return rule.equals(target);
        }
    }

    /**
     * 全局扫描过期key，此方法应当被定时调用
     */
    public void scanExpiredKeys(){
        getExpired().forEach((k, v)-> this.getExpired(k));
    }

    private Map<String, Object> getData(){
        return Catch.data;
    }

    private Map<String, Long> getExpired(){
        return Catch.expired;
    }

    private static boolean has = false;

    private JwCatchTemplate(){
        synchronized (JwCatchTemplate.class){
            //防止通过反射实例化
            if(!has){
                has = true;
            }else{
                throw new RuntimeException("强制单例，不允许再次实例化");
            }
        }
    }

    public synchronized static JwCatchTemplate getInstance() {
        return Builder.INSTANCE;
    }

    private static final class Builder {
        private static final JwCatchTemplate INSTANCE = new JwCatchTemplate();
    }

}
